In [ ]:
import numpy as np
import pandas as pd
from bqplot import (
    Axis, ColorAxis, LinearScale, DateScale, DateColorScale, OrdinalScale,
    OrdinalColorScale, ColorScale, Scatter, Lines, Figure, Tooltip
)
from ipywidgets import Label

Get Data


In [ ]:
price_data = pd.DataFrame(np.cumsum(np.random.randn(150, 2).dot([[1.0, -0.8], [-0.8, 1.0]]), axis=0) + 100,
                          columns=['Security 1', 'Security 2'], index=pd.date_range(start='01-01-2007', periods=150))
size = 100
np.random.seed(0)
x_data = range(size)
y_data = np.cumsum(np.random.randn(size) * 100.0)
ord_keys = np.array(['A', 'B', 'C', 'D', 'E', 'F'])
ordinal_data = np.random.randint(5, size=size)

In [ ]:
symbols = ['Security 1', 'Security 2']

dates_all = price_data.index.values
dates_all_t = dates_all[1:]
sec1_levels = np.array(price_data[symbols[0]].values.flatten())
log_sec1 = np.log(sec1_levels)
sec1_returns = log_sec1[1:] - log_sec1[:-1]

sec2_levels = np.array(price_data[symbols[1]].values.flatten())

Basic Scatter


In [ ]:
sc_x = DateScale()
sc_y = LinearScale()

scatt = Scatter(x=dates_all, y=sec2_levels, scales={'x': sc_x, 'y': sc_y})
ax_x = Axis(scale=sc_x, label='Date')
ax_y = Axis(scale=sc_y, orientation='vertical', tick_format='0.0f', label='Security 2')

Figure(marks=[scatt], axes=[ax_x, ax_y])

Changing the marker and adding text to each point of the scatter


In [ ]:
# Changing the marker as 
sc_x = LinearScale()
sc_y = LinearScale()

scatt = Scatter(x=x_data[:10], y=y_data[:10], names=np.arange(10),
                scales={'x': sc_x, 'y': sc_y}, colors=['red'], marker='cross')
ax_x = Axis(scale=sc_x)
ax_y = Axis(scale=sc_y, orientation='vertical', tick_format='0.2f')

Figure(marks=[scatt], axes=[ax_x, ax_y], padding_x=0.025)

Changing the opacity of each marker


In [ ]:
scatt.default_opacities = [0.3, 0.5, 1.]

Representing additional dimensions of data

Linear Scale for Color Data


In [ ]:
sc_x = DateScale()
sc_y = LinearScale()

sc_c1 = ColorScale()
scatter = Scatter(x=dates_all, y=sec2_levels, color=sec1_returns,
                  scales={'x': sc_x, 'y': sc_y, 'color': sc_c1}, 
                  stroke='black')

ax_y = Axis(label='Security 2', scale=sc_y, 
            orientation='vertical', side='left')

ax_x = Axis(label='Date', scale=sc_x, num_ticks=10, label_location='end')
ax_c = ColorAxis(scale=sc_c1, tick_format='0.2%', label='Returns', orientation='vertical', side='right')

m_chart = dict(top=50, bottom=70, left=50, right=100)

Figure(axes=[ax_x, ax_c, ax_y], marks=[scatter], fig_margin=m_chart,
       title='Scatter of Security 2 vs Dates')

In [ ]:
## Changing the default color. 
scatter.colors = ['blue']  # In this case, the dot with the highest X changes to blue.

In [ ]:
## setting the fill to be empty
scatter.stroke = None
scatter.fill = False

In [ ]:
## Setting the fill back
scatter.stroke = 'black'
scatter.fill = True

In [ ]:
## Changing the color to a different variable
scatter.color = sec2_levels
ax_c.tick_format = '0.0f'
ax_c.label = 'Security 2'

In [ ]:
## Changing the range of the color scale
sc_c1.colors = ['blue', 'green', 'orange']

Date Scale for Color Data


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()

sc_c1 = DateColorScale(scheme='Reds')
scatter = Scatter(x=sec2_levels, y=sec1_levels, color=dates_all,
                  scales={'x': sc_x, 'y': sc_y, 'color': sc_c1}, default_size=128,
                  stroke='black')

ax_y = Axis(label='Security 1 Level', scale=sc_y, orientation='vertical', side='left')

ax_x = Axis(label='Security 2', scale=sc_x)
ax_c = ColorAxis(scale=sc_c1, label='Date', num_ticks=5)

m_chart = dict(top=50, bottom=80, left=50, right=50)
Figure(axes=[ax_x, ax_c, ax_y], marks=[scatter], fig_margin=m_chart)

Ordinal Scale for Color


In [ ]:
factor = int(np.ceil(len(sec2_levels) * 1.0 / len(ordinal_data)))
ordinal_data = np.tile(ordinal_data, factor)

c_ord = OrdinalColorScale(colors=['DodgerBlue', 'SeaGreen', 'Yellow', 'HotPink', 'OrangeRed'])
sc_x = LinearScale()
sc_y = LinearScale()

scatter2 = Scatter(x=sec2_levels[1:],
                   y=sec1_returns,
                   color=ordinal_data,
                   scales={'x': sc_x, 'y': sc_y, 'color': c_ord}, 
                   legend='__no_legend__',
                   stroke='black')

ax_y = Axis(label='Security 1 Returns', scale=sc_y, orientation='vertical', tick_format='.0%')

ax_x = Axis(label='Security 2', scale=sc_x, label_location='end')
ax_c = ColorAxis(scale=c_ord, label='Class', side='right', orientation='vertical')

m_chart = dict(top=50, bottom=70, left=100, right=100)
Figure(axes=[ax_x, ax_y, ax_c], marks=[scatter2], fig_margin=m_chart)

In [ ]:
ax_c.tick_format = '0.2f'
c_ord.colors = ['blue', 'red', 'green', 'yellow', 'orange']

Setting size and opacity based on data


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()
sc_y2 = LinearScale()

sc_size = LinearScale()
sc_opacity = LinearScale()


scatter2 = Scatter(x=sec2_levels[1:], y=sec1_levels, size=sec1_returns,
                   scales={'x': sc_x, 'y': sc_y, 'size': sc_size, 'opacity': sc_opacity}, 
                   default_size=128, colors=['orangered'], stroke='black')

ax_y = Axis(label='Security 1', scale=sc_y, orientation='vertical', side='left')
ax_x = Axis(label='Security 2', scale=sc_x)

Figure(axes=[ax_x, ax_y], marks=[scatter2])

In [ ]:
## Changing the opacity of the scatter
scatter2.default_opacities = [0.5, 0.3, 0.1]

In [ ]:
## Resetting the size for the scatter
scatter2.size=None

In [ ]:
## Resetting the opacity and setting the opacity according to the date
scatter2.default_opacities = [1.0]

In [ ]:
scatter2.opacity = dates_all

Changing the skew of the marker


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()
sc_e = LinearScale()

scatter = Scatter(scales={'x': sc_x, 'y': sc_y, 'skew': sc_e}, 
                  x=sec2_levels[1:], y=sec1_levels,
                  skew=sec1_returns, stroke="black",
                  colors=['gold'], default_size=200, 
                  marker='rectangle', default_skew=0)

ax_y = Axis(label='Security 1', scale=sc_y, orientation='vertical', side='left')
ax_x = Axis(label='Security 2', scale=sc_x)

Figure(axes=[ax_x, ax_y], marks=[scatter], animation_duration=1000)

In [ ]:
scatter.skew = None

In [ ]:
scatter.skew = sec1_returns

Rotation scale


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()
sc_e = LinearScale()
sc_c = ColorScale(scheme='Reds')
x1 = np.linspace(-1, 1, 30)
y1 = np.linspace(-1, 1, 30)
x, y = np.meshgrid(x1,y1)
x, y = x.flatten(), y.flatten()
rot = x**2 + y**2
color=x-y
scatter = Scatter(scales={'x': sc_x, 'y': sc_y, 'color': sc_c, 'rotation': sc_e}, 
                  x=x, y=y, rotation=rot, color=color,
                  stroke="black", default_size=200, 
                  marker='arrow', default_skew=0.5,)
Figure(marks=[scatter], animation_duration=1000)

In [ ]:
scatter.rotation = 1.0 / (x ** 2 + y ** 2 + 1)

Scatter Chart Interactions

Moving points in Scatter


In [ ]:
## Enabling moving of points in scatter. Try to click and drag any of the points in the scatter and 
## notice the line representing the mean of the data update
sc_x = LinearScale()
sc_y = LinearScale()

scat = Scatter(x=x_data[:10], y=y_data[:10], scales={'x': sc_x, 'y': sc_y}, colors=['orange'],
               enable_move=True)
lin = Lines(x=[], y=[], scales={'x': sc_x, 'y': sc_y}, line_style='dotted', colors=['orange'])

def update_line(change=None):
    with lin.hold_sync():
        lin.x = [np.min(scat.x), np.max(scat.x)]
        lin.y = [np.mean(scat.y), np.mean(scat.y)]

update_line()
# update line on change of x or y of scatter
scat.observe(update_line, names=['x'])
scat.observe(update_line, names=['y'])

ax_x = Axis(scale=sc_x)
ax_y = Axis(scale=sc_y, tick_format='0.2f', orientation='vertical')

fig = Figure(marks=[scat, lin], axes=[ax_x, ax_y])
fig

Updating X and Y while moving the point


In [ ]:
## In this case on drag, the line updates as you move the points.
scat.update_on_move = True

In [ ]:
latex_widget = Label(color='Green', font_size='16px')

def callback_help(name, value):
    latex_widget.value = str(value)
    
latex_widget

In [ ]:
scat.on_drag_start(callback_help)

In [ ]:
scat.on_drag(callback_help)

In [ ]:
scat.on_drag_end(callback_help)

In [ ]:
## Restricting movement to only along the Y-axis
scat.restrict_y = True

Adding/Deleting points


In [ ]:
## Enabling adding the points to Scatter. Try clicking anywhere on the scatter to add points
with scat.hold_sync():
    scat.enable_move = False
    scat.interactions = {'click': 'add'}

Switching between interactions


In [ ]:
from ipywidgets import ToggleButtons, VBox

interact_control = ToggleButtons(options=['Add', 'Delete', 'Drag XY', 'Drag X', 'Drag Y'],
                                 style={'button_width': '120px'})

def change_interact(change):
    interact_parameters = {
     'Add': {'interactions': {'click': 'add'},
             'enable_move': False},
     'Delete': {'interactions': {'click': 'delete'},
                'enable_move': False},
     'Drag XY': {'interactions': {'click': None},
                 'enable_move': True,
                 'restrict_x': False,
                 'restrict_y': False},
     'Drag X': {'interactions': {'click': None},
                'enable_move': True,
                'restrict_x': True,
                'restrict_y': False},
     'Drag Y': {'interactions': {'click': None},
                'enable_move': True,
                'restrict_x': False,
                'restrict_y': True}
    }
    for param, value in interact_parameters[interact_control.value].items():
        setattr(scat, param, value)
        
interact_control.observe(change_interact, names='value')

fig.title = 'Adding/Deleting/Moving points'
VBox([fig, interact_control])

Custom event on end of drag


In [ ]:
## Whenever drag is ended, there is a custom event dispatched which can be listened to.
## try dragging a point and see the data associated with the event being printed
def test_func(self, content):
    print("received drag end", content)

scat.on_drag_end(test_func)

Adding tooltip and custom hover style


In [ ]:
x_sc = LinearScale()
y_sc = LinearScale()

x_data = x_data[:50]
y_data = y_data[:50]

def_tt = Tooltip(fields=['x', 'y'], formats=['', '.2f'])

scatter_chart = Scatter(x=x_data, y=y_data, scales= {'x': x_sc, 'y': y_sc}, colors=['dodgerblue'],
                        tooltip=def_tt, unhovered_style={'opacity': 0.5})
ax_x = Axis(scale=x_sc)
ax_y = Axis(scale=y_sc, orientation='vertical', tick_format='0.2f')

Figure(marks=[scatter_chart], axes=[ax_x, ax_y])

In [ ]:
## removing field names from the tooltip
def_tt.show_labels = False

In [ ]:
## changing the fields displayed in the tooltip
def_tt.fields = ['y']

In [ ]:
def_tt.fields = ['x']